#ifndef MQSERVER_MQ_EXCHANGE_HPP_
#define MQSERVER_MQ_EXCHANGE_HPP_
#include "../mqcomm/mq_logger.hpp"
#include "../mqcomm/mq_helper.hpp"
#include "../mqcomm/proto/mq_msg.pb.h"
#include <iostream>
#include <unordered_map>
#include <memory>
#include <mutex>


namespace mqserver
{
class Exchange;
class ExchangeManager;
using ExchangePtr = std::shared_ptr<Exchange>;
using ExchangePtrMap = std::unordered_map<std::string, ExchangePtr>; 
using ExchangeManagerPtr = std::shared_ptr<ExchangeManager>;

// 交换机数据类
class Exchange 
{
public:
    Exchange() = default;
    Exchange(const std::string& name, 
            proto::message::ExchangeType type, 
            bool durable, bool auto_delete, 
            const google::protobuf::Map<std::string, std::string>& args)
        : name_(name), type_(type), durable_(durable), auto_delete_(auto_delete), args_(args) 
    {}

    //在存储数据库时，对args序列化存储 key=val&key=val....
    std::string getArgs()
    {
        return std::move(helper::StringHelper::serializeToString(args_));
    }
    // 反序列化args_str
    void setArgs(const std::string& args_str)
    {
        helper::StringHelper::deserializeFromString(args_str, &args_);       
    }

public:
    std::string name_;           // 交换机名称
    proto::message::ExchangeType type_;   // 交换机类型
    bool durable_;               // 是否持久化标志
    bool auto_delete_;           // 是否自动删除标志
    google::protobuf::Map<std::string, std::string> args_;      // 其它参数
};

// 交换机数据持久化管理类
class ExchangeMapper
{
public:
    ExchangeMapper(const std::string& dbfile) 
        : sql_helper_(dbfile)
    {
        // 1. 检查数据库文件的父级路径, 不存在创建
        std::string path = helper::FileHelper::parentDirectory(dbfile);
        helper::FileHelper::createDirectory(path);
        assert(sql_helper_.open());
        // 2. 建表
        createTable();
    }
    ~ExchangeMapper() { sql_helper_.close(); }
    
    void createTable()       // 创建交换机数据表
    {
        const std::string create_table_sql =
            "create table if not exists exchange_table( \
            name varchar(32) primary key, \
            type int, \
            durable int, \
            auto_delete int, \
            args varchar(128));";

        if(sql_helper_.exec(create_table_sql, nullptr, nullptr) == false)
        {
            LOG_FATAL << "create exchange_table fail!\n";
            abort();
        }
    }

    void removeTabel()       // 删表
    {
        const std::string drop_table_sql = "drop table if exists exchange_table";
        if(sql_helper_.exec(drop_table_sql, nullptr, nullptr) == false)
        {
            LOG_FATAL << "drop exchange_table fail!\n";
            abort();
        }
    }

    bool insert(ExchangePtr& exchange)          // 新增交换机数据
    {
        const std::string insert =
            "insert into exchange_table values('%s', %d, %d, %d, '%s')";
        std::string args_str = exchange->getArgs();
        char insert_sql[4096] = {0};
        sprintf(insert_sql, insert.c_str(), exchange->name_.c_str(), exchange->type_,\
                exchange->durable_, exchange->auto_delete_, args_str.c_str());

        return sql_helper_.exec(insert_sql, nullptr, nullptr);
    }
    
    bool remove(const std::string& name)              // 删数据
    {
        const std::string delete_sql = "delete from exchange_table \
                                        where name = '" + name + "'";
        return sql_helper_.exec(delete_sql, nullptr, nullptr);
    }
    ExchangePtrMap recover()                    // 恢复所有交换机数据
    {
        ExchangePtrMap result;
        const std::string select_sql = "select name, type, durable, auto_delete, args \
                                        from exchange_table;";
        
        sql_helper_.exec(select_sql, selectCallback, (void*)&result);
        return result;
    }

private:
    static int selectCallback(void* arg, int col_count, char** row, char** fields_name)
    {
        ExchangePtrMap* result = static_cast<ExchangePtrMap*>(arg);
        auto exp = std::make_shared<Exchange>();
        exp->name_ = row[0];
        exp->type_ = static_cast<proto::message::ExchangeType>(std::stoi(row[1]));
        exp->durable_ = static_cast<bool>(std::stoi(row[2]));
        exp->auto_delete_ = static_cast<bool>(std::stoi(row[3]));
        if(row[4]) exp->setArgs(row[4]);

        result->emplace(exp->name_, exp);
        return 0;        // 返回0才代表成功
    }

private:
    helper::SqliteHelper sql_helper_;
};

// 交换机数据内存管理类(对外提供访问)
class ExchangeManager
{
public:
    ExchangeManager(const std::string& dbfile) 
        : mapper_(dbfile) { exchanges_ = mapper_.recover(); }

    bool declareExchange(const std::string& name,       // 声明交换机
            proto::message::ExchangeType type, 
            bool durable, bool auto_delete, 
            const google::protobuf::Map<std::string, std::string>& args)
    {
        std::lock_guard<std::mutex> lock(mtx_);
        if(existsNoLock(name)) return true;          // 交换机已经存在, 直接返回
        ExchangePtr exp = std::make_shared<Exchange>(name, type, durable, auto_delete, args);
        if(durable == true) 
        {
            if(mapper_.insert(exp) == false) 
                return false;
        }
        auto result = exchanges_.emplace(name, exp);
        return true;
    }
    void deleteExchange(const std::string& name)        // 删除交换机
    {
        std::lock_guard<std::mutex> lock(mtx_);
        if(!existsNoLock(name)) return; 
        if(exchanges_[name]->durable_ == true) mapper_.remove(name);
        exchanges_.erase(name);
    }
    ExchangePtr selectExchange(const std::string& name)  // 获取指定交换机
    {
        std::lock_guard<std::mutex> lock(mtx_);
        auto it = exchanges_.find(name);
        return (it == exchanges_.end() ? ExchangePtr() : it->second);
    }
    bool exists(const std::string& name)        
    {
        std::lock_guard<std::mutex> lock(mtx_);
        auto it = exchanges_.find(name);
        return it != exchanges_.end();
    }
    size_t size()
    {
        std::lock_guard<std::mutex> lock(mtx_);
        return exchanges_.size();
    }
    void clear()    // 销毁所有交换机数据
    {
        std::lock_guard<std::mutex> lock(mtx_);
        mapper_.removeTabel();
        exchanges_.clear();
    }
private:
    bool existsNoLock(const std::string& name) const 
    {
        auto it = exchanges_.find(name);
        return it != exchanges_.end();      // 找到了为真
    }
private:
    std::mutex mtx_;
    ExchangeMapper mapper_;
    ExchangePtrMap exchanges_;
};
  
} // namespace exchange

#endif